package edu.csl.study.springboot.common.kit;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;

/**
 * 
 * 
 * apache的 BeanUtils: 性能最差,不建议使用 spring的 BeanUtils: spring支持忽略某些属性不进行映射 cglib的
 * BeanCopier:基于ASM 最快
 * Dozer :在定制化的属性映射方面做得比较好的有
 * Dozer支持简单属性映射、复杂类型映射、双向映射、隐式映射以及递归映射。可使用xml或者注解进行映射的配置，支持自动类型转换，使用方便。
 *   但Dozer底层是使用reflect包下 Field类的 set(Object obj, Object value)，性能低下 
 * Orika：底层采用了javassist类库生成Bean映射的字节码 cglib BeanCopier > Orika> spring > Dozer > apache
 * 
 * 总结：当属性名和属性类型完全相同时，选择cglib BeanCopier 和 Spring BeanUtils 均可。 极端性能要求，当然是用cglib
 * BeanCopier。 当存在属性名称不同或者属性名称相同但属性类型不同的情况时，选择Orika
 * 
 * 这里选用cglib BeanCopier 和 Orika
 * 
 *  注意：BeanCopier属于浅拷贝（属性为引用时只拷贝引用，这样修改时会相互影响）
 *      上面的拷贝，只有Orika属于深拷贝。
 *  Orika 非常强大，不一样的字段都能映射。List加不一样的字段也能映射。
 *  
 *  List<Student> students = mapperFactory.getMapperFacade().mapAsList(personList, Student.class);
 * 
 * 
 * @author CSL
 *
 */
public final class BeanKit {

	/**
	 * The map to store {@link BeanCopier} of source type and class type for
	 * conversion.
	 */
	private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();
	 
	/**
     * Orika - 缓存MapperFacade，为了提高性能而且不必频繁创建
     */
    private static Map<String, MapperFacade> CACHE_MAPPERFACADE_MAP = new ConcurrentHashMap<>();
    /**
     * Orika - 定义MapperFactory
     */
	public final static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
	
	
	/**
	 * 深度克隆自己
	 * @param source
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T deepClone(Object source) {
		
		return (T) mapperFactory.getMapperFacade().map(source, source.getClass());
	}
	
	/**
	 * 深度拷贝：
	 * 对于基本类型，不同类型之间能转换。
	 * 对于为空的，也会拷贝null。
	 * @param source
	 * @param target
	 */
	public static void deepCopy(Object source, Object target) {
		
		deepCopy(source,target,null);
	}
	
    
	
	/**
	 * 深度拷贝
	 * 对于基本类型，不同类型之间能转换。
	 * 对于为空的，也会拷贝null。
	 *  
	 * @param source
	 * @param resultClass
	 * @return
	 */
	public static <T> T deepCopy(Object source, Class<T> resultClass) {
		if(source == null){
			return null;
		}
		return mapperFactory.getMapperFacade().map(source, resultClass);
	}
	/**
	 * 深度拷贝：
	 * 对于基本类型，不同类型之间能转换。
	 * 对于为空的，也会拷贝null。
	 * 注意：如果目标对象的属性在原对象不存在，也没有配置映射关系，那么目标对象的属性值会保留。
	 * 扩张：
	 *    这里的fileld默认是能相互映射的。如果要单向，可以使用
	 *    .fieldAToB("name", "fullName")
	 *   或 .fieldBToA("name", "fullName")
	 * @param source
	 * @param target
	 * @param filedMapping 类型不同时的映射
	 */
	public static void deepCopy(Object source, Object target,Map<String,String> filedMapping ) {
		
		getMapperFacade(source.getClass(),target.getClass(),filedMapping).map(source, target);
	}
	
	
	/**
	 * 深度拷贝
	 * 对于基本类型，不同类型之间能转换。
	 * 对于为空的，也会拷贝null。
	 * @param source
	 * @param toClass
	 * @param filedMapping
	 * @return
	 */
	public static <T> T deepCopy(Object source, Class<T> toClass,Map<String,String> filedMapping) {
		if(source == null){
			return null;
		}
		return getMapperFacade(source.getClass(),toClass,filedMapping).map(source, toClass);
	}
	
	private static <S,T>  MapperFacade getMapperFacade(Class<S> sourceClazz,Class<T> toClazz,Map<String,String> filedMapping){
		//缓存转换的类
		String mapKey = sourceClazz.getClass().getCanonicalName() + "_" + toClazz.getCanonicalName();
        MapperFacade mapperFacade = CACHE_MAPPERFACADE_MAP.get(mapKey);
        if (mapperFacade == null) {
        	//如果有映射字段，那么缓存MapperFacade，下次可以直接使用。
        	if(filedMapping != null){
        		MapperFactory factory = new DefaultMapperFactory.Builder().build();
                ClassMapBuilder<? extends Object, ? extends Object> classMapBuilder  = factory.classMap(sourceClazz.getClass(), toClazz);
                filedMapping.forEach(classMapBuilder::field);
                classMapBuilder.byDefault().register();
                mapperFacade = factory.getMapperFacade();
                CACHE_MAPPERFACADE_MAP.put(mapKey, mapperFacade);
        	}else{
        		//如果没有映射字段，那么直接用定义的常量mapperFactory。
        		mapperFacade = mapperFactory.getMapperFacade();
        	}
        }
		return mapperFacade;
		
	}
	/**
	 * 深度拷贝List
	 * 
	 * @param sourceList
	 * @param resultClass
	 * @return
	 */
	public static <V,T> List<T> deepCopyList(List<V> sourceList, Class<T> resultClass ) {
		if(sourceList == null){
			return null;
		}
		return mapperFactory.getMapperFacade().mapAsList(sourceList, resultClass);
	}
	/**
	 * 深度拷贝List
	 * 
	 * @param sourceList
	 * @param sourceClass  获取不到参数V的实参类型，所以只能多定义一个参数
	 * @param resultClass
	 * @param filedMapping
	 * @return
	 */
	public static <V,T> List<T> deepCopyList(List<V> sourceList,Class<V> sourceClass, Class<T> resultClass,Map<String,String> filedMapping) {
		if(sourceList == null){
			return null;
		}
		return getMapperFacade(sourceClass,resultClass,filedMapping).mapAsList(sourceList, resultClass);
	}
	/**
	 * 拷贝对象： 浅层次的拷贝 
	 * （1）如果是基本类型数据，那么拷贝数据
	 * （2）如果属性是对象或者数组等引用类型，那么拷贝的是引用。即：拷贝后，复制对象修改这种属性会影响原对象；同样原对象修改也会影响复制对象。
	 * 注意点：属性类型不同拷贝不了，这时候可以用转换器。见测试用例。
	 * @param source 源对象
	 * @param target 目标对象
	 */
	public static void copy(Object source, Object target) {
		
		copy(source, target, null);
	}
    /**
     * 带转换器
     * @param source
     * @param target
     * @param converter
     */
	public static void copy(Object source, Object target, Converter converter) {
		Objects.requireNonNull(source, "source must not be null");
		Objects.requireNonNull(target, "target must not be null");

		BeanCopier beanCopier = getBeanCopier(source.getClass(), target.getClass());
		if (converter == null) {
			beanCopier.copy(source, target, null);
		} else {
			beanCopier.copy(source, target, converter);
		}

	}

	/**
	 * /**根据原对象生成一个对象： 同样需要注意浅层次复制的问题
	 * 
	 * @param source
	 * @param clazz
	 * @return
	 */
	public static <T> T copy(Object source, Class<T> clazz) {
		if(source == null){
			return null;
		}
		return copy(source, clazz, null);
	}

	/**
	 * 根据原对象生成一个对象： 同样需要注意浅层次复制的问题
	 * 
	 * @param source
	 *            源对象
	 * @param clazz
	 *            要转换的类
	 * @return
	 */
	public static <T> T copy(Object source, Class<T> clazz, Converter converter) {
		if(source == null){
			return null;
		}
		T result;
		try {
			result = clazz.newInstance();
		} catch (ReflectiveOperationException e) {
			throw new RuntimeException("fail to create instance of type" + clazz.getCanonicalName(), e);
		}

		copy(source, result, converter);
		return result;
	}

	private static BeanCopier getBeanCopier(Class<?> source, Class<?> target) {
		String key = generateKey(source, target);
		return BEAN_COPIER_MAP.computeIfAbsent(key, x -> BeanCopier.create(source, target, false));
	}

	
	private static String generateKey(Class<?> source, Class<?> target) {
		return source.getCanonicalName().concat(target.getCanonicalName());
	}

	public static void main(String[] args) {
            
	}

}
